package ch.zhaw.skuh.turing.model.turingmaschine.rules;

import ch.zhaw.skuh.turing.model.turingmaschine.NG_Factorial_N_ary;

public class NG_Factorial extends NG_Multiplication {

	public NG_Factorial(NG_Factorial_N_ary tc) {
		super(tc);
	}

	@Override
	protected void addRulePart_EndMultiplication(int q, int exitNormal,
			int[] tapes, int marker) {
		String m = Integer.toString(marker, radix);
		tc.addRulePadded(q, "___" + m, exitNormal, "____", "1010", tapes);
	}

	@Override
	public int setRules(int minState, int exitNormal, int exitSpecial,
			int[] tapes) {

		// *** multiplication rules ***

		int[] tapesCurrent;
		int currentState = minState;
		int marker = 0;
		int marker2 = 1;

		tapesCurrent = new int[] { tapes[0], tapes[1], tapes[2], tapes[3] };

		// q0 to q(radix-1)
		// multiplication, exit to qX where X is overrun
		currentState = addRule_MultplyOverRunToQ(minState,
				minState + radix + 1, tapesCurrent, marker);

		// qX (x=radix)
		// prepare for multiplication, store trailing zeros to t4 and set marker
		currentState = addRule_HandleTrailingZeros(currentState, tapesCurrent,
				marker);

		// qX+1 (x=radix)
		// correct positions to marker
		currentState = addRule_CorrectPositionToM(currentState,
				currentState + 1, tapesCurrent, marker);

		// qX+2
		// find end of t1 (forwards)
		tapesCurrent = new int[] { tapes[0] };
		currentState = addRule_MoveEndT1(currentState, currentState + 1,
				tapesCurrent);

		// qX+3
		// subtract 1 of t1
		tapesCurrent = new int[] { tapes[0], tapes[1], tapes[2], tapes[3] };
		currentState = addRule_SubtractOneOfT1(currentState, minState + radix,
				currentState + 1, tapesCurrent, marker, marker2);

		// qX+4 (x=radix)
		// move trailing "0" from t4 to t2
		currentState = addRule_MoveTrailingZeros(currentState, exitNormal,
				tapesCurrent, marker);

		return currentState;
	}

	// find end of t1 (forwards)
	private int addRule_MoveEndT1(int q, int exitNormal, int[] tapes) {
		// continue while t1 has chars, rest is blank
		for (int iT1 = 0; iT1 < radix; iT1++) {
			String t1 = Integer.toString(iT1, radix);
			tc.addRulePadded(q, t1, q, t1, "1", tapes);
		}
		// end --> exit
		tc.addRulePadded(q, "_", exitNormal, "_", "-1", tapes);

		return q + 1;
	}

	// subtract 1 of t1
	private int addRule_SubtractOneOfT1(int q, int exitNormal, int exitSpecial,
			int[] tapes, int marker, int marker2) {
		String m = Integer.toString(marker, radix);
		String n = Integer.toString(marker2, radix);

		String t2 = "_";
		String t3 = "_";
		String t4 = "_";

		String tOld;
		String tNew;

		for (int iT1 = 0; iT1 < radix; iT1++) {
			// no marker, subtract
			tOld = Integer.toString(iT1, radix) + t2 + t3 + t4;

			// can subtract 1
			if (iT1 > 2) {
				// not-null --> set marker 3
				tNew = Integer.toString(iT1 - 1, radix) + t2 + m + t4;
				tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);
			} else if (iT1 != 0) {
				// leading 0 or possible value="1", set marker and continue checking for value=1
				tNew = Integer.toString(iT1 - 1, radix) + m + t3 + t4;
				tc.addRulePadded(q, tOld, q, tNew, "-1000", tapes);
			} else if (iT1 == 0) {
				// overrun --> continue
				tNew = Integer.toString(radix - 1, radix) + t2 + t3 + t4;
				tc.addRulePadded(q, tOld, q, tNew, "-1000", tapes);
			}

			// with marker t2, checking backwards for more chars
			tOld = Integer.toString(iT1, radix) + m + t3 + t4;
			// char found, value not 1, go forward --> set marker 3
			tNew = Integer.toString(iT1, radix) + t2 + m + t4;
			tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);

			// marker 2 AND 3, move forward, delete leading 0
			tOld = Integer.toString(iT1, radix) + m + m + t4;
			if (iT1 > 1) {
				// remove marker t2 --> continue
				tNew = Integer.toString(iT1, radix) + t2 + m + t4;
				tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);
			} else
			// special check : "1" as first char, lets check if there is
			// anything more (value != "1")
			if (iT1 == 1) {
				// set marker2 to t2 --> continue
				tNew = Integer.toString(iT1, radix) + n + t3 + t4;
				tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);
			} else {
				// leading 0, remove --> continue
				tNew = "_" + m + m + t4;
				tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);
			}

			// with marker t3, continue move to start
			tOld = Integer.toString(iT1, radix) + t2 + m + t4;
			tNew = new String(tOld);
			tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);

			// with marker 2 on t2, continue move to start
			tOld = Integer.toString(iT1, radix) + n + t3 + t4;
			tNew = Integer.toString(iT1, radix) + t2 + m + t4;
			tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);

		}
		tNew = "_" + t2 + t3 + t4;

		// end of t1 after substraction, delete marker && exit --> exit
		tOld = "_" + t2 + m + t4;
		tc.addRulePadded(q, tOld, exitNormal, tNew, "-1-100", tapes);

		// value="1" found, delete markers && exit --> exit
		tOld = "_" + n + t3 + t4;
		tc.addRulePadded(q, tOld, exitSpecial, tNew, "0001", tapes);

		// start of t1, check for value=1 and leading 0 --> continue
		tOld = "_" + m + t3 + t4;
		tNew = "_" + m + m + t4;
		tc.addRulePadded(q, tOld, q, tNew, "1000", tapes);

		return q + 1;
	}

}
